package com.xinting.piano.lib.network;

import com.squareup.okhttp.Callback;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;

import java.io.File;
import java.io.IOException;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.CookieStore;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by wuxinting on 15/10/28.
 */
public class PNetWork {

    private static PNetWork netWork;

    private final OkHttpClient client = new OkHttpClient();
    private Map<String, String> header;

    private PNetWork() {
        header = new HashMap<>();
    }

    public static PNetWork getInstance() {
        if (netWork == null) {
            synchronized (PNetWork.class) {
                if (netWork == null) {
                    netWork = new PNetWork();
                }
            }
        }
        return netWork;
    }

    /**
     * 打开Cookie
     */
    public void enableCookie(CookieStore store) {
        CookieManager cm = new CookieManager(store, CookiePolicy.ACCEPT_ALL);
        client.setCookieHandler(cm);
    }

    /***
     * Add header options of post
     * @param key
     * @param value
     */
    public void addHeader(String key, String value) {
        header.put(key, value);
    }

    public void addHeaderAll(Map<? extends String, ? extends String> kvs) {
        header.putAll(kvs);
    }

    /**
     * get method of http
     * @param url
     */
    public void get(String url, final IHandleHttpResult handler) throws IOException {
        Request.Builder builder = new Request.Builder();
        builder.url(url);

        client.newCall(builder.build()).enqueue(new HandleResult(handler));
    }

    /**
     * get method of http sync
     * @param url
     * @param params
     * @return
     * @throws IOException
     */
    public Response getSync(String url, Map<String, String> params) throws IOException {
        return client.newCall(_get(url, params)).execute();
    }

    private Request _get(String url, Map<String, String> params) {
        Request.Builder builder = new Request.Builder();

        if (params == null) {
            return builder.url(url).build();
        }

        StringBuilder sb = new StringBuilder(url);
        if (url.indexOf("?") != -1) {
            //has params
            sb.append('&');
        } else {
            sb.append('?');
        }

        for (Map.Entry<String, String> entry : params.entrySet()) {
            sb.append(entry.getKey());
            sb.append('=');
            sb.append(entry.getValue());
            sb.append('&');
        }
        sb.deleteCharAt(sb.length());

        builder.url(sb.toString());

        return builder.build();
    }

    /**
     * post method of http
     * @param url
     * @param params
     * @throws IOException
     */
    private Request _post(String url, Map<String, String> params) throws IOException {

        //Create post body
        FormEncodingBuilder builder = new FormEncodingBuilder();
        if (params != null) {
            for (Map.Entry entry : params.entrySet()) {
                builder.add(entry.getKey().toString(), entry.getValue().toString());
            }
        }
        RequestBody body = builder.build();

        //Add Header
        Request.Builder rBuilder =  new Request.Builder();
        rBuilder.url(url);
        rBuilder.header("User-Agent", "Piano Application");
        for (Map.Entry entry : header.entrySet()) {
            Object v = entry.getValue();
            rBuilder.addHeader(entry.getKey().toString(), v == null ? "null" : v.toString());
        }
        header.clear();
        Request request = rBuilder.post(body).build();
        return request;
    }

    /**
     * post method of http. async
     * @param url
     * @param params
     * @param handler
     */
    public void post(String url, Map<String, String> params, final IHandleHttpResult handler) throws IOException {
        Request request = _post(url, params);
        client.newCall(request).enqueue(new HandleResult(handler));
    }

    /**
     * post method of http. sync
     * @param url
     * @param params
     * @return
     */
    public Response postSync(String url, Map<String, String> params) throws IOException {
        Request request = _post(url, params);
        return client.newCall(request).execute();
    }

    /**
     * post file sync
     * @param url
     * @param file
     */
    public String postFile(String url, File file, Map<String, String> params) {
        return postFile(url, file, params, null);
    }

    /**
     * post file sync
     * @param url url upload to
     * @param file file to upload
     * @param params params to post
     * @param progress progress callback {@link com.xinting.piano.lib.network.PNetWork.IProgress}
     * @return
     */
    public String postFile(String url, final File file, Map<String, String> params, final IProgress progress) {
        final HttpFileUpload hfu = new HttpFileUpload(url, file);
        hfu.setProgress(progress);
        String rs = hfu.Sending(params);

        return rs;
    }

    /**
     * 统一返回处理类
     */
    private class HandleResult implements Callback {

        private IHandleHttpResult handleHttpResult;
        public HandleResult(IHandleHttpResult handleHttpResult) {
            this.handleHttpResult = handleHttpResult;
        }

        @Override
        public void onFailure(Request request, IOException e) {
            if (handleHttpResult != null) {
                handleHttpResult.onRequest(request, e);
            }
        }

        @Override
        public void onResponse(Response response) throws IOException {
            if (handleHttpResult != null) {
                handleHttpResult.onResponse(response);
            }
        }
    }

    /**
     * interface of handle result while get&post return back
     */
    public interface IHandleHttpResult {

        /**
         * handle failed
         * @param req
         */
        void onRequest(Request req, IOException e);

        /**
         * handle success
         * @param res
         * @throws IOException
         */
        void onResponse(Response res) throws IOException;
    }

    /**
     * 进度接口
     */
    public interface IProgress {

        /**
         * 进度
         * @param cur 当前
         * @param max 总体大小
         */
        void onProgress(long cur, long max);
    }
}
